unit StateMachine;

interface

type
  TState = ( sLetter, sBlank, sCR, sLF, sFinish );

  TStateEvent = procedure( Sender : TObject; const AText : string; AState : TState ) of object;

  TStateMachine = class
  private
    FPosition : Integer;
    FText :     string;
    FMax :      Integer;
    FOnStep :   TStateEvent;
    procedure SetText( const Value : string );
    procedure SetOnStep( const Value : TStateEvent );
    procedure DoStep( const AText : string; AState : TState );
  public
    property Text :     string read FText write SetText;
    property Position : Integer read FPosition;
    property Max :      Integer read FMax;

    property OnStep : TStateEvent read FOnStep write SetOnStep;

    procedure Reset;
    procedure Step;
  end;

implementation

{ TStateMachine }

procedure TStateMachine.DoStep( const AText : string; AState : TState );
begin
  if Assigned( OnStep )
  then
    OnStep( Self, AText, AState );
end;

procedure TStateMachine.Reset;
begin
  FMax      := Length( FText );
  FPosition := 0;
end;

procedure TStateMachine.SetOnStep( const Value : TStateEvent );
begin
  FOnStep := Value;
end;

procedure TStateMachine.SetText( const Value : string );
begin
  FText := Value;
  Reset;
end;

procedure TStateMachine.Step;
var
  LText :  string;
  LState : TState;
begin
  if FPosition > FMax
  then
    Exit;

  if FPosition = FMax
  then
    begin
      Inc( FPosition );
      DoStep( FText, sFinish );
      Exit;
    end;

  Inc( FPosition );

  LText := Copy( FText, 1, FPosition );
  case FText[FPosition] of
    ' ' :
      LState := sBlank;
    #13 :
      LState := sCR;
    #10 :
      LState := sLF;
  else
    LState := sLetter;
  end;

  DoStep( LText, LState );
end;

end.
